package fr.xgouchet.wedding.client.service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;

import com.google.gwt.core.client.JsArray;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.MultiWordSuggestOracle;

import fr.xgouchet.wedding.client.jsni.JSGuest;
import fr.xgouchet.wedding.shared.model.Guest;
import fr.xgouchet.wedding.shared.model.GuestDetails;

/**
 * JSON service dedicated to Guests info
 * 
 * @author x.gouchet
 * 
 */
public class GuestsService {

	/**
	 * @return the suggestion for guests roles
	 */
	public static MultiWordSuggestOracle getRoles() {
		MultiWordSuggestOracle roles = new MultiWordSuggestOracle();
		for (String role : GUESTS_ROLES)
			roles.add(role);
		return roles;
	}

	/**
	 * Fetches a specific guest
	 * 
	 * @param id
	 *            the id of the guest to fetch
	 * @param asyncCallback
	 *            the callback to which the data will be transmitted
	 */
	public static void getGuest(String id, AsyncCallback<Guest> asyncCallback) {
		if ((GUESTS != null) && (GUESTS.containsKey(id))) {
			asyncCallback.onSuccess(GUESTS.get(id));
			return;
		}

		// TODO fetch it from server
		asyncCallback.onFailure(new Throwable());
	}

	/**
	 * Fetch the guests list
	 * 
	 * @param asyncCallback
	 *            the callback
	 */
	public static void getGuestsList(
			final AsyncCallback<ArrayList<GuestDetails>> asyncCallback) {
		if ((GUESTS != null) && (GUESTS.size() != 0)) {
			asyncCallback.onSuccess(getGuestsDetailsList());
			return;
		}

		String url = URL.encode(JSON_URL_GUESTS_LIST);

		// Send request to server and catch any errors.
		RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, url);
		builder.setHeader("Content-type", "application/x-www-form-urlencoded");
		// builder.setHeader("X-XSRF-Cookie", Cookies.getCookie("myCookieKey"));

		try {
			builder.sendRequest(null, new RequestCallback() {
				public void onError(Request request, Throwable exception) {
					asyncCallback.onFailure(new Throwable(
							"Couldn't retrieve JSON (Request Error)"));
				}

				public void onResponseReceived(Request request,
						Response response) {
					if (200 == response.getStatusCode()) {
						toCachedData(asArrayOfGuests(response.getText()));
						asyncCallback.onSuccess(getGuestsDetailsList());
					} else {
						asyncCallback.onFailure(new Throwable(
								"Couldn't retrieve JSON (Reponse status : "
										+ response.getStatusText() + ")"));
					}
				}
			});
		} catch (RequestException e) {
			asyncCallback.onFailure(new Throwable("Couldn't send request"));
		}
	}

	/**
	 * Saves the given guest in the server
	 * 
	 * @param guest
	 *            the guest to save
	 * @param asyncCallback
	 *            the callback to which the data will be transmitted
	 */
	public static void saveGuest(Guest guest,
			final AsyncCallback<Object> asyncCallback) {
		String url = URL.encode(JSON_URL_GUEST_SAVE);

		// Send request to server and catch any errors.
		RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, url);
		builder.setHeader("Content-type", "application/x-www-form-urlencoded");
		// builder.setHeader("X-XSRF-Cookie", Cookies.getCookie("myCookieKey"));

		// add post data
		String postData = (guest.getID().length() > 0) ? ("id=" + guest.getID() + "&")
				: "";
		postData += "firstname=" + guest.getFirstName() + "&";
		postData += "lastname=" + guest.getLastName() + "&";
		postData += "nickname=" + guest.getNickname() + "&";
		postData += "sex=" + (guest.isMan() ? "1" : "0") + "&";
		postData += "age=" + guest.getAge() + "&";
		postData += "status=" + guest.getStatus() + "&";
		postData += "comingwith=" + guest.getComingWith() + "&";
		postData += "roles=" + guest.getRoles() + "&";
		postData += "group=" + guest.getGroup() + "&";
		postData += "table=" + guest.getTable() + "&";
		postData += "seat=" + guest.getSeat();
		postData = URL.encode(postData);

		try {
			builder.sendRequest(postData, new RequestCallback() {
				public void onError(Request request, Throwable exception) {
					asyncCallback.onFailure(new Throwable(
							"Couldn't retrieve JSON (Request Error)"));
				}

				public void onResponseReceived(Request request,
						Response response) {
					if (200 == response.getStatusCode()) {
						toCachedData(asArrayOfGuests(response.getText()));
						asyncCallback.onSuccess(getGuestsDetailsList());
					} else {
						asyncCallback.onFailure(new Throwable(
								"Couldn't retrieve JSON (Reponse status : "
										+ response.getStatusText() + ")"));
					}
				}
			});
		} catch (RequestException e) {
			asyncCallback.onFailure(new Throwable("Couldn't send request"));
		}
	}

	/**
	 * Deletes a specific guest
	 * 
	 * @param id
	 *            the id of the guest to delete
	 * @param asyncCallback
	 *            the callback to which the data will be transmitted
	 */
	public static void deleteGuest(String id,
			final AsyncCallback<Object> asyncCallback) {
		// Remove guest from cache
		GUESTS.remove(id);

		// delete guest from server
		String url = URL.encode(JSON_URL_GUEST_DELETE);

		// Send request to server and catch any errors.
		RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, url);
		builder.setHeader("Content-type", "application/x-www-form-urlencoded");
		// builder.setHeader("X-XSRF-Cookie", Cookies.getCookie("myCookieKey"));

		// Add post data
		String postData = URL.encode("id=" + id);

		try {
			builder.sendRequest(postData, new RequestCallback() {
				public void onError(Request request, Throwable exception) {
					asyncCallback.onFailure(new Throwable(
							"Couldn't retrieve JSON (Request Error)"));
				}

				public void onResponseReceived(Request request,
						Response response) {
					if (200 == response.getStatusCode()) {
						asyncCallback.onSuccess(null);
					} else {
						asyncCallback.onFailure(new Throwable(
								"Couldn't retrieve JSON (Reponse status : "
										+ response.getStatusText() + ")"));
					}
				}
			});
		} catch (RequestException e) {
			asyncCallback.onFailure(new Throwable("Couldn't send request"));
		}
	}

	/**
	 * Convert the string of JSON into JavaScript object.
	 * 
	 * @param json
	 *            json
	 * @return return
	 */
	private final static native JsArray<JSGuest> asArrayOfGuests(String json) /*-{
		return eval(json);
	}-*/;

	/**
	 * @param data
	 *            the JSON data
	 */
	private final static void toCachedData(JsArray<JSGuest> data) {
		if (GUESTS == null)
			GUESTS = new HashMap<String, Guest>();
		for (int i = 0; i < data.length(); i++) {
			addGuestToCache(data.get(i));
		}
	}

	/**
	 * @param from
	 *            the JavaScript object
	 */
	private static void addGuestToCache(JSGuest from) {
		Guest to = new Guest();
		to.setID(from.getID());
		to.setFirstName(from.getFirstName());
		to.setLastName(from.getLastName());
		to.setAge(from.getAge());
		to.setSex(from.getSex());
		to.setStatus(from.getStatus());
		to.setComingWith(from.getComingWith());
		to.setRoles(from.getRoles());
		to.setGroup(from.getGroup());
		to.setSeat(from.getTable(), from.getSeat());
		GUESTS.put(from.getID(), to);
	}

	/**
	 * @return the guests details list only
	 */
	private static ArrayList<GuestDetails> getGuestsDetailsList() {
		ArrayList<GuestDetails> list = new ArrayList<GuestDetails>();
		Collection<Guest> collec = GUESTS.values();
		Iterator<Guest> it = collec.iterator();

		String id, display = "";
		while (it.hasNext()) {
			Guest guest = it.next();
			id = guest.getID();
			display = guest.getDisplayName();
			list.add(new GuestDetails(id, display));
		}

		return list;
	}

	/** Local cached data */
	private static HashMap<String, Guest> GUESTS = null;

	/** List of known roles */
	private static final String[] GUESTS_ROLES = new String[] { "Mariée",
			"Marié", "Témoin", "Photographe", "Maitre de cérémonie", };

	/** Root URL */
	private static final String JSON_URL_ROOT = "http://localhost/GWT/gwt_on-dit-oui/";

	/** URL of the service listing the guests */
	private static final String JSON_URL_GUESTS_LIST = JSON_URL_ROOT
			+ "list_guests.php";

	/** URL of the service listing the guests */
	private static final String JSON_URL_GUEST_SAVE = JSON_URL_ROOT
			+ "save_guest.php";

	/** URL of the service deleting guests */
	private static final String JSON_URL_GUEST_DELETE = JSON_URL_ROOT
			+ "delete_guest.php";
}
